package com.rsscollector.controller.admin.action;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Iterator;
import java.util.zip.Adler32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Logger;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import com.rsscollector.util.Log4JUtil;

public class GetLogFilesAction extends Action {

    private static Logger logger = Logger
            .getLogger(GetLogFilesAction.class.getName());

    @Override
    public ActionForward execute(ActionMapping mapping, ActionForm form,
                                 HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        ActionForward next = null;
        String logFileName = request.getParameter("logFileName");
        FileAppender foundLogFileAppender = null;
        Collection<FileAppender> fileAppenders = Log4JUtil.getAllFileAppenders();

        /* Make sure that there is an appender with that log file name (for security) */
        for (Iterator<FileAppender> it = fileAppenders.iterator(); it.hasNext();) {
            FileAppender fileAppender = it.next();
            String aLogFile = fileAppender.getFile();
            if (aLogFile != null && aLogFile.equals(logFileName)) {
                foundLogFileAppender = fileAppender;
                break;
            }
        }

        if (foundLogFileAppender == null) {
            logger.debug("Cannot find a FileAppender");
            /* Invalid parameter, go back to input page*/
            next = mapping.getInputForward();
        } else {

            String logName = foundLogFileAppender.getFile();
            logger.debug("Find FileAppender:" + logName);
            File logFile = new File(logName);
            File logFolder = logFile.getParentFile();
            logger.debug("Log folder:" + logFolder.getAbsolutePath());

            File tmpDir = createTempDir(logFolder);
            moveFilesWithPrefix(logName, tmpDir);

            File tmpFile = File.createTempFile("logarchive", ".zip", logFolder);
            zipFolder(tmpDir, tmpFile);

            serveFile(tmpFile, request, response);

            // clean up
            FileUtils.forceDelete(tmpFile);
            FileUtils.forceDelete(tmpDir);
        }
        return next;
    }

    private void serveFile(File tmpFile, HttpServletRequest request, HttpServletResponse response)
            throws Exception {

        String mimetype = "application/zip";
        response.setContentType(mimetype);
        response.setContentLength((int) tmpFile.length());
        response.setHeader("Content-Disposition", "attachment; filename=" + tmpFile.getName());
        InputStream is = new FileInputStream(tmpFile);
        IOUtils.copy(is, response.getOutputStream());
        is.close();
    }

    private void zipFolder(File tmpDir, File tmpFile) throws Exception {
        int BUFFER = 2048;

        FileOutputStream dest = new FileOutputStream(tmpFile);
        CheckedOutputStream checksum = new CheckedOutputStream(dest, new Adler32());
        ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(checksum));
        out.setMethod(ZipOutputStream.DEFLATED);
        byte data[] = new byte[BUFFER];
        // get a list of files from current directory
        String files[] = tmpDir.list();

        for (int i = 0; i < files.length; i++) {
            FileInputStream fi = new FileInputStream(new File(tmpDir, files[i]));
            BufferedInputStream origin = new BufferedInputStream(fi, BUFFER);
            ZipEntry entry = new ZipEntry(files[i]);
            out.putNextEntry(entry);
            int count;
            while ((count = origin.read(data, 0, BUFFER)) != -1) {
                out.write(data, 0, count);
            }
            origin.close();
        }
        out.close();
    }

    /**
     * Creates temp dir for a logName (of a file appender)
     *
     * @param logFolder directory to contain
     * @return newly created directory
     * @throws IOException
     */
    private File createTempDir(File logFolder) throws IOException {
        File tmpFile = File.createTempFile("tmpLogDir", "", logFolder);
        String tmpDirName = tmpFile.getName();
        tmpFile.delete();
        File tmpDir = new File(logFolder, tmpDirName);
        tmpDir.mkdir();
        logger.debug("Created temp dir:" + tmpDir.getAbsolutePath());
        return tmpDir;
    }

    private void moveFilesWithPrefix(String logName, File dir) throws IOException {
        File logFile = new File(logName);
        String logFileName = logFile.getName();
        //logName = logFile.getCanonicalPath(); 
        File logFolder = logFile.getParentFile();
        for (String childFileName : logFolder.list()) {
            File childFile = new File(logFolder, childFileName);
            //childFileName = childFile.getCanonicalPath();
            if (childFileName.startsWith(logFileName)) {
                logger.debug("Process log file:" + childFileName);
                File destFile;
                destFile = new File(dir, childFileName);
                try {
                    logger.debug("Will rename " + childFile.getAbsolutePath() + " to " + destFile.getAbsolutePath());
                    childFile.renameTo(destFile);
                } catch (Exception e) {
                }
                if (!destFile.exists()) {
                    logger.debug("Cannot rename, will copy " + childFile.getAbsolutePath() + " to " + destFile.getAbsolutePath());
                    try {
                        FileInputStream fi = new FileInputStream(childFile);
                        FileOutputStream fo = new FileOutputStream(destFile);
                        IOUtils.copy(fi, fo);
                        fi.close();
                        fo.close();
                    } catch (Exception e) {
                    }
                    childFile.delete();
                }
            }
        }
    }

}
